/*
* @file recoder.cpp
* @brief This file contains the implementation of the Recorder class
* @author feiniao
* @date 2025-5-07
* @version 1.0
* @note This code is designed to work with ESP32-S3 and uses the ESP-IDF 5.2.3 framework.
*/



#include "recoder.h"


/*
* @brief Initialize SPIFFS (SPI Flash File System)
*        This function mounts the SPIFFS filesystem and logs the total and used space.
*/
void Recorder::init_spiffs()
{
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = "storage",
        .max_files = 5,
        .format_if_mount_failed = true
    };

    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "SPIFFS挂载失败: %s", esp_err_to_name(ret));
        return;
    }
    size_t total = 0, used = 0;
    esp_spiffs_info("storage", &total, &used);
    ESP_LOGI(TAG, "SPIFFS空间: 已用=%dKB, 总共=%dKB", used/1024, total/1024);
}


/*
* @brief Format SPIFFS (SPI Flash File System)
*        This function unmounts the SPIFFS filesystem and formats it.
*        It clears all data stored in the SPIFFS partition.
* @note This function should be used with caution as it will erase all data in the SPIFFS partition.
*       It is typically used for debugging or when the filesystem is corrupted.
* @return None
*/
void Recorder::format_spiffs() {
    // 卸载 SPIFFS（必须先卸载才能格式化）
    esp_vfs_spiffs_unregister("storage");

    // 格式化 SPIFFS 分区
    esp_err_t ret = esp_spiffs_format("storage");
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "SPIFFS 格式化失败: %s", esp_err_to_name(ret));
        return;
    }
    ESP_LOGI(TAG, "SPIFFS 格式化完成，所有数据已清空");

    // 挂载 SPIFFS
    init_spiffs(); // 调用你原来的挂载函数
}

/*
* @brief Constructor for the Recorder class

* @return None
*/
Recorder::Recorder(): recorder(INMP441_BCLK_PIN, INMP441_WS_PIN, INMP441_SD_PIN, -1, SAMPLE_RATE, Microphone_I2S_Port, (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), I2S_BITS_PER_SAMPLE_16BIT)
{   
    ESP_LOGI("Recorder", "Recorder is initializing...");
    static bool spiffs_initialized = false;
    if(!spiffs_initialized){
        format_spiffs(); // 仅执行一次
        spiffs_initialized = true;
    }
    ESP_LOGI("Recorder", "Recorder initialized");
    clear_recorder_File();
}


/*
* @brief destructor for the recorder class
* @note This function unmounts the SPIFFS filesystem and clears the recorder file.
*       It is called when the Recorder object goes out of scope or is deleted.
*       It ensures that all resources are properly released and the filesystem is unmounted.
* @return None
*/
Recorder::~Recorder()
{   
    clear_recorder_File();
    esp_vfs_spiffs_unregister("storage");
    ESP_LOGI(TAG, "SPIFFS unmounted");
    ESP_LOGI("Recorder", "Recorder destroyed");
}


/*
* @brief record audio for a specified duration
* @param seconds Duration in seconds to record audio
* @note This function creates a PCM file and writes audio samples to it.
*       It uses the BSP_I2S interface to read audio samples and writes them to the file.
*       The recording stops when the specified duration is reached or if an error occurs.

*/
size_t Recorder::int32t_record(float seconds)
{   
    char full_path[64];
    snprintf(full_path, sizeof(full_path), "%s", PCM_FILE_NAME);
    FILE* f = fopen(full_path, "wb");
    if (!f) {
        ESP_LOGE(TAG, "[FILE] PCM文件打开失败!");
        return 0;
    }

    const size_t target_size = seconds * SAMPLE_RATE * sizeof(int32_t);
    size_t bytes_written = 0;
    ESP_LOGI(TAG, "开始录音");
    while (bytes_written < target_size) {
        int32_t sample = recorder.int32t_read();
        size_t written = fwrite(&sample, sizeof(int32_t), 1, f);
        if (written != 1) {
            ESP_LOGE(TAG, "写入文件失败");
            break;
        }
        bytes_written += written *sizeof(int32_t);
    }
    ESP_LOGI(TAG, "录音完成，写入数据: %zu 字节", bytes_written);
    fclose(f);

    return bytes_written / sizeof(int32_t); // 返回样本数
}


/*
* @brief record audio for a specified duration
* @param seconds Duration in seconds to record audio
* @note This function creates a PCM file and writes audio samples to it.
*       It uses the BSP_I2S interface to read audio samples and writes them to the file.
*       The recording stops when the specified duration is reached or if an error occurs.
* @version 1.1 change the sample to int16_t
*/
size_t Recorder::int16t_record(float seconds)
{   
    char full_path[64];
    snprintf(full_path, sizeof(full_path), "%s", PCM_FILE_NAME);
    FILE* f = fopen(full_path, "wb");
    if (!f) {
        ESP_LOGE(TAG, "[FILE] PCM文件打开失败!");
        return 0;
    }

    const size_t target_size = seconds * SAMPLE_RATE * sizeof(int16_t);
    size_t bytes_written = 0;
    ESP_LOGI(TAG, "开始录音");
    while (bytes_written < target_size) {
        int16_t sample = recorder.int16t_read();
        size_t written = fwrite(&sample, sizeof(int16_t), 1, f);
        if (written != 1) {
            ESP_LOGE(TAG, "写入文件失败");
            break;
        }
        bytes_written += written * sizeof(int16_t);
    }
    ESP_LOGI(TAG, "录音完成，写入数据: %zu 字节", bytes_written);
    fclose(f);

    return bytes_written / sizeof(int16_t); // 返回样本数
}


/*
* @brief read PCM file and store the data in the buffer
* @param buffer Pointer to the buffer where the PCM data will be stored
* @note This function opens the PCM file in read mode, reads the data into the buffer,
*       and returns the number of samples read. It also logs the file size and any errors encountered.
*/
void Recorder::read_pcm_file_int32t(int32_t* buffer)
{   
    FILE* f = fopen(PCM_FILE_NAME, "rb");
    if (!f) {
        ESP_LOGE(TAG, "文件打开失败: %s", PCM_FILE_NAME);
        return;
    }

    /*
    移动到文件末尾以获取文件大小。
    读取当前指针的位置（即文件大小）。
    将指针重置到文件开头，以便后续操作（如读取文件内容）。
    */
    fseek(f, 0, SEEK_END);
    long file_size = ftell(f);
    fseek(f, 0, SEEK_SET);

    size_t size_read = fread(buffer, sizeof(int32_t), file_size / sizeof(int32_t), f);
    if (size_read == 0) {
        ESP_LOGE(TAG, "读取文件失败或文件为空");
    } else {
        ESP_LOGI(TAG, "读取文件成功: %s, 读取总字节数: %zu", PCM_FILE_NAME, size_read * sizeof(int32_t));
        ESP_LOGI(TAG, "读取样本数: %d 个", size_read);
    }

    fclose(f);
}



/*
* @brief read PCM file and store the data in the buffer
* @param buffer Pointer to the buffer where the PCM data will be stored
* @note This function opens the PCM file in read mode, reads the data into the buffer,
*       and returns the number of samples read. It also logs the file size and any errors encountered.
*/
void Recorder::read_pcm_file_int16t(int16_t* buffer)
{   
    FILE* f = fopen(PCM_FILE_NAME, "rb");
    if (!f) {
        ESP_LOGE(TAG, "文件打开失败: %s", PCM_FILE_NAME);
        return;
    }

    /*
    移动到文件末尾以获取文件大小。
    读取当前指针的位置（即文件大小）。
    将指针重置到文件开头，以便后续操作（如读取文件内容）。
    */
    fseek(f, 0, SEEK_END);
    long file_size = ftell(f);
    fseek(f, 0, SEEK_SET);

    size_t size_read = fread(buffer, sizeof(int16_t), file_size / sizeof(int16_t), f);
    if (size_read == 0) {
        ESP_LOGE(TAG, "读取文件失败或文件为空");
    } else {
        ESP_LOGI(TAG, "读取文件成功: %s, 读取总字节数: %zu", PCM_FILE_NAME, size_read * sizeof(int16_t));
        ESP_LOGI(TAG, "读取样本数: %d 个", size_read);
    }

    fclose(f);
}


/*
* @brief clear the recorder file
* @note This function clears the PCM file by opening it in write mode and closing it immediately.
*       This effectively truncates the file to zero length.
* @return None
*/
void Recorder::clear_recorder_File()
{
    char full_path[64];
    snprintf(full_path, sizeof(full_path), "%s", PCM_FILE_NAME);
    FILE* f = fopen(full_path, "wb");
    if (!f) {
        ESP_LOGE(TAG, "[FILE] PCM文件打开失败!");
        return;
    }
    fclose(f); 
    ESP_LOGI(TAG, "[FILE] PCM文件已清空: %s", PCM_FILE_NAME);
}



/*
* @brief read a single sample from the I2S interface
* @return The 16-bit sample read from the I2S interface
* @note This function uses the BSP_I2S interface to read a single audio sample.
*       It is typically used for real-time audio processing or analysis.
* @return The 16-bit sample read from the I2S interface
*/
int16_t Recorder::read_single_Sample_int16t()
{
    return recorder.int16t_read();
}


/*
* @brief read a single sample from the I2S interface
* @return The 32-bit sample read from the I2S interface
* @note This function uses the BSP_I2S interface to read a single audio sample.
*       It is typically used for real-time audio processing or analysis.
* @return The 32-bit sample read from the I2S interface
*/
int32_t Recorder::read_single_Sample_int32t()
{
    return recorder.int32t_read();
}